from decimal import Decimal
from django.utils import timezone
from django.db.models import Q
from typing import Dict, List, Tuple, Optional, Union
import logging
import json
from datetime import datetime, timedelta
import math

from .models import StopLossSetting, PortfolioHolding, Portfolio
from data_service.models import FundNavDetail

logger = logging.getLogger(__name__)

class StopLossEngine:
    """
    止损管理引擎
    实现三种止损策略：固定比例止损、移动止损、技术指标止损
    """
    
    def __init__(self):
        self.technical_indicators = {
            'ma': self._calculate_moving_average,
            'rsi': self._calculate_rsi,
            'macd': self._calculate_macd,
            'bollinger': self._calculate_bollinger_bands
        }
    
    def check_all_stop_loss_conditions(self) -> Dict[str, List[Dict]]:
        """
        检查所有激活的止损条件
        
        Returns:
            触发的止损设置字典
        """
        triggered_stop_losses = {
            'fixed_ratio': [],
            'trailing': [],
            'technical': []
        }
        
        try:
            # 获取所有激活的止损设置
            active_settings = StopLossSetting.objects.filter(
                status='active'
            ).select_related('portfolio', 'product')
            
            for setting in active_settings:
                try:
                    # 获取当前价格
                    current_price = self._get_current_price(setting.product)
                    if current_price is None:
                        continue
                    
                    # 根据策略类型检查止损条件
                    if setting.strategy_type == 'fixed_ratio':
                        if self._check_fixed_ratio_stop_loss(setting, current_price):
                            triggered_stop_losses['fixed_ratio'].append({
                                'setting': setting,
                                'current_price': current_price,
                                'trigger_reason': '固定比例止损触发'
                            })
                    
                    elif setting.strategy_type == 'trailing':
                        if self._check_trailing_stop_loss(setting, current_price):
                            triggered_stop_losses['trailing'].append({
                                'setting': setting,
                                'current_price': current_price,
                                'trigger_reason': '移动止损触发'
                            })
                    
                    elif setting.strategy_type == 'technical':
                        if self._check_technical_stop_loss(setting, current_price):
                            triggered_stop_losses['technical'].append({
                                'setting': setting,
                                'current_price': current_price,
                                'trigger_reason': '技术指标止损触发'
                            })
                
                except Exception as e:
                    logger.error(f"检查止损设置失败 {setting.id}: {e}")
                    continue
            
            return triggered_stop_losses
            
        except Exception as e:
            logger.error(f"检查止损条件失败: {e}")
            return triggered_stop_losses
    
    def _check_fixed_ratio_stop_loss(self, setting: StopLossSetting, current_price: Decimal) -> bool:
        """
        检查固定比例止损条件
        
        Args:
            setting: 止损设置
            current_price: 当前价格
            
        Returns:
            是否触发止损
        """
        try:
            if not setting.stop_loss_ratio or not setting.purchase_price:
                return False
            
            # 计算止损价格
            stop_loss_price = setting.purchase_price * (Decimal('1') - setting.stop_loss_ratio)
            
            # 更新止损价格到设置中
            if setting.stop_loss_price != stop_loss_price:
                setting.stop_loss_price = stop_loss_price
                setting.save(update_fields=['stop_loss_price'])
            
            # 检查是否触发止损
            return current_price <= stop_loss_price
            
        except Exception as e:
            logger.error(f"检查固定比例止损失败: {e}")
            return False
    
    def _check_trailing_stop_loss(self, setting: StopLossSetting, current_price: Decimal) -> bool:
        """
        检查移动止损条件
        
        Args:
            setting: 止损设置
            current_price: 当前价格
            
        Returns:
            是否触发止损
        """
        try:
            if not setting.trailing_distance:
                return False
            
            # 更新最高价格
            if setting.highest_price is None or current_price > setting.highest_price:
                setting.highest_price = current_price
                setting.save(update_fields=['highest_price'])
            
            # 计算移动止损价格
            trailing_stop_price = setting.highest_price * (Decimal('1') - setting.trailing_distance)
            
            # 检查是否触发止损
            return current_price <= trailing_stop_price
            
        except Exception as e:
            logger.error(f"检查移动止损失败: {e}")
            return False
    
    def _check_technical_stop_loss(self, setting: StopLossSetting, current_price: Decimal) -> bool:
        """
        检查技术指标止损条件
        
        Args:
            setting: 止损设置
            current_price: 当前价格
            
        Returns:
            是否触发止损
        """
        try:
            if not setting.technical_indicator or not setting.indicator_params:
                return False
            
            indicator_name = setting.technical_indicator.lower()
            params = setting.indicator_params if isinstance(setting.indicator_params, dict) else json.loads(setting.indicator_params)
            
            # 获取历史价格数据
            price_data = self._get_price_history(setting.product, days=params.get('period', 20))
            if not price_data:
                return False
            
            # 计算技术指标
            if indicator_name in self.technical_indicators:
                indicator_value = self.technical_indicators[indicator_name](price_data, params)
                
                # 根据指标类型判断止损条件
                return self._evaluate_technical_condition(indicator_name, indicator_value, current_price, params)
            
            return False
            
        except Exception as e:
            logger.error(f"检查技术指标止损失败: {e}")
            return False
    
    def _get_current_price(self, product) -> Optional[Decimal]:
        """
        获取产品当前价格
        
        Args:
            product: 产品对象
            
        Returns:
            当前价格
        """
        try:
            # 从基金净值数据获取最新价格
            latest_nav = FundNavDetail.objects.filter(
                 fund_code=product.fund_code
             ).order_by('-nav_date').first()
            
            if latest_nav:
                 return latest_nav.unit_nav
            
            # 如果没有净值数据，尝试从持仓获取当前价格
            latest_holding = PortfolioHolding.objects.filter(
                product=product
            ).order_by('-updated_at').first()
            
            if latest_holding:
                return latest_holding.current_price
            
            return None
            
        except Exception as e:
            logger.error(f"获取当前价格失败: {e}")
            return None
    
    def _get_price_history(self, product, days: int = 20) -> List[Decimal]:
        """
        获取产品历史价格数据
        
        Args:
            product: 产品对象
            days: 获取天数
            
        Returns:
            价格列表
        """
        try:
            end_date = timezone.now().date()
            start_date = end_date - timedelta(days=days)
            
            nav_data = FundNavDetail.objects.filter(
                fund_code=product.fund_code,
                nav_date__gte=start_date,
                nav_date__lte=end_date
            ).order_by('nav_date').values_list('unit_nav', flat=True)
            
            return list(nav_data)
            
        except Exception as e:
            logger.error(f"获取历史价格失败: {e}")
            return []
    
    def _calculate_moving_average(self, prices: List[Decimal], params: Dict) -> Optional[Decimal]:
        """
        计算移动平均线
        
        Args:
            prices: 价格列表
            params: 参数字典，包含period
            
        Returns:
            移动平均值
        """
        try:
            period = params.get('period', 20)
            if len(prices) < period:
                return None
            
            recent_prices = prices[-period:]
            return Decimal(str(sum(recent_prices) / len(recent_prices)))
            
        except Exception as e:
            logger.error(f"计算移动平均线失败: {e}")
            return None
    
    def _calculate_rsi(self, prices: List[Decimal], params: Dict) -> Optional[Decimal]:
        """
        计算相对强弱指标(RSI)
        
        Args:
            prices: 价格列表
            params: 参数字典，包含period
            
        Returns:
            RSI值
        """
        try:
            period = params.get('period', 14)
            if len(prices) < period + 1:
                return None
            
            # 计算价格变化
            price_changes = []
            for i in range(1, len(prices)):
                price_changes.append(prices[i] - prices[i-1])
            
            # 分离上涨和下跌
            gains = [max(change, Decimal('0')) for change in price_changes[-period:]]
            losses = [abs(min(change, Decimal('0'))) for change in price_changes[-period:]]
            
            # 计算平均收益和损失
            avg_gain = sum(gains) / period
            avg_loss = sum(losses) / period
            
            if avg_loss == 0:
                return Decimal('100')
            
            rs = avg_gain / avg_loss
            rsi = Decimal('100') - (Decimal('100') / (Decimal('1') + rs))
            
            return rsi
            
        except Exception as e:
            logger.error(f"计算RSI失败: {e}")
            return None
    
    def _calculate_macd(self, prices: List[Decimal], params: Dict) -> Optional[Dict]:
        """
        计算MACD指标
        
        Args:
            prices: 价格列表
            params: 参数字典，包含fast_period, slow_period, signal_period
            
        Returns:
            MACD字典
        """
        try:
            fast_period = params.get('fast_period', 12)
            slow_period = params.get('slow_period', 26)
            signal_period = params.get('signal_period', 9)
            
            if len(prices) < slow_period:
                return None
            
            # 计算EMA
            def calculate_ema(data, period):
                multiplier = Decimal('2') / (period + 1)
                ema = [data[0]]
                for i in range(1, len(data)):
                    ema.append((data[i] * multiplier) + (ema[-1] * (Decimal('1') - multiplier)))
                return ema
            
            fast_ema = calculate_ema(prices, fast_period)
            slow_ema = calculate_ema(prices, slow_period)
            
            # 计算MACD线
            macd_line = [fast_ema[i] - slow_ema[i] for i in range(len(slow_ema))]
            
            # 计算信号线
            if len(macd_line) < signal_period:
                return None
            
            signal_line = calculate_ema(macd_line, signal_period)
            
            return {
                'macd': macd_line[-1],
                'signal': signal_line[-1],
                'histogram': macd_line[-1] - signal_line[-1]
            }
            
        except Exception as e:
            logger.error(f"计算MACD失败: {e}")
            return None
    
    def _calculate_bollinger_bands(self, prices: List[Decimal], params: Dict) -> Optional[Dict]:
        """
        计算布林带指标
        
        Args:
            prices: 价格列表
            params: 参数字典，包含period, std_dev
            
        Returns:
            布林带字典
        """
        try:
            period = params.get('period', 20)
            std_dev = Decimal(str(params.get('std_dev', 2)))
            
            if len(prices) < period:
                return None
            
            recent_prices = prices[-period:]
            
            # 计算移动平均
            ma = sum(recent_prices) / len(recent_prices)
            
            # 计算标准差
            variance = sum([(float(price) - float(ma)) ** 2 for price in recent_prices]) / len(recent_prices)
            std = Decimal(str(math.sqrt(variance)))
            
            # 计算布林带
            upper_band = Decimal(str(ma)) + (std * std_dev)
            lower_band = Decimal(str(ma)) - (std * std_dev)
            
            return {
                'upper': upper_band,
                'middle': ma,
                'lower': lower_band
            }
            
        except Exception as e:
            logger.error(f"计算布林带失败: {e}")
            return None
    
    def _evaluate_technical_condition(self, indicator_name: str, indicator_value, 
                                    current_price: Decimal, params: Dict) -> bool:
        """
        评估技术指标止损条件
        
        Args:
            indicator_name: 指标名称
            indicator_value: 指标值
            current_price: 当前价格
            params: 参数字典
            
        Returns:
            是否触发止损
        """
        try:
            if indicator_value is None:
                return False
            
            condition_type = params.get('condition', 'below')
            threshold = Decimal(str(params.get('threshold', 0)))
            
            if indicator_name == 'ma':
                # 移动平均线止损：价格跌破均线
                if condition_type == 'below':
                    return current_price < Decimal(str(indicator_value))
                elif condition_type == 'above':
                    return current_price > Decimal(str(indicator_value))
            
            elif indicator_name == 'rsi':
                # RSI止损：超卖或超买
                if condition_type == 'below':
                    return Decimal(str(indicator_value)) < threshold  # 超卖
                elif condition_type == 'above':
                    return Decimal(str(indicator_value)) > threshold  # 超买
            
            elif indicator_name == 'macd':
                # MACD止损：死叉或金叉
                if condition_type == 'death_cross':
                    return Decimal(str(indicator_value['macd'])) < Decimal(str(indicator_value['signal']))
                elif condition_type == 'golden_cross':
                    return Decimal(str(indicator_value['macd'])) > Decimal(str(indicator_value['signal']))
            
            elif indicator_name == 'bollinger':
                # 布林带止损：突破上轨或下轨
                if condition_type == 'below_lower':
                    return current_price < Decimal(str(indicator_value['lower']))
                elif condition_type == 'above_upper':
                    return current_price > Decimal(str(indicator_value['upper']))
            
            return False
            
        except Exception as e:
            logger.error(f"评估技术指标条件失败: {e}")
            return False
    
    def trigger_stop_loss(self, setting: StopLossSetting, current_price: Decimal, 
                         trigger_reason: str) -> Dict[str, any]:
        """
        触发止损
        
        Args:
            setting: 止损设置
            current_price: 当前价格
            trigger_reason: 触发原因
            
        Returns:
            执行结果
        """
        try:
            # 更新止损设置状态
            setting.status = 'triggered'
            setting.triggered_at = timezone.now()
            setting.save()
            
            result = {
                'success': True,
                'setting_id': setting.id,
                'portfolio_id': setting.portfolio.id,
                'product_code': setting.product.code,
                'trigger_price': float(current_price),
                'trigger_reason': trigger_reason,
                'trigger_time': setting.triggered_at.isoformat(),
                'auto_execute': setting.auto_execute
            }
            
            # 如果设置了自动执行，则执行卖出操作
            if setting.auto_execute:
                execution_result = self._execute_stop_loss_order(setting, current_price)
                result.update(execution_result)
            
            logger.info(f"止损触发成功: {setting.id} - {trigger_reason}")
            return result
            
        except Exception as e:
            logger.error(f"触发止损失败: {e}")
            return {
                'success': False,
                'error': str(e),
                'setting_id': setting.id
            }
    
    def _execute_stop_loss_order(self, setting: StopLossSetting, current_price: Decimal) -> Dict[str, any]:
        """
        执行止损卖出订单
        
        Args:
            setting: 止损设置
            current_price: 当前价格
            
        Returns:
            执行结果
        """
        try:
            # 获取持仓信息
            holding = PortfolioHolding.objects.filter(
                portfolio=setting.portfolio,
                product=setting.product
            ).first()
            
            if not holding:
                return {
                    'execution_success': False,
                    'execution_error': '未找到对应持仓'
                }
            
            # 计算卖出金额
            sell_amount = holding.shares * current_price
            
            # 更新持仓（这里简化处理，实际应该创建交易记录）
            holding.current_price = current_price
            holding.current_amount = Decimal('0')
            holding.shares = Decimal('0')
            holding.save()
            
            # 更新止损设置状态
            setting.status = 'executed'
            setting.save()
            
            return {
                'execution_success': True,
                'sell_amount': float(sell_amount),
                'execution_time': timezone.now().isoformat()
            }
            
        except Exception as e:
            logger.error(f"执行止损订单失败: {e}")
            return {
                'execution_success': False,
                'execution_error': str(e)
            }
    
    def create_stop_loss_setting(self, portfolio_id: int, product_id: int, 
                               strategy_data: Dict) -> Dict[str, any]:
        """
        创建止损设置
        
        Args:
            portfolio_id: 投资组合ID
            product_id: 产品ID
            strategy_data: 策略数据
            
        Returns:
            创建结果
        """
        try:
            portfolio = Portfolio.objects.get(id=portfolio_id)
            
            # 验证策略数据
            validation_result = self._validate_strategy_data(strategy_data)
            if not validation_result['valid']:
                return {
                    'success': False,
                    'error': validation_result['error']
                }
            
            # 创建止损设置
            setting = StopLossSetting.objects.create(
                portfolio_id=portfolio_id,
                product_id=product_id,
                **strategy_data
            )
            
            return {
                'success': True,
                'setting_id': setting.id,
                'message': '止损设置创建成功'
            }
            
        except Exception as e:
            logger.error(f"创建止损设置失败: {e}")
            return {
                'success': False,
                'error': str(e)
            }
    
    def _validate_strategy_data(self, strategy_data: Dict) -> Dict[str, any]:
        """
        验证策略数据
        
        Args:
            strategy_data: 策略数据
            
        Returns:
            验证结果
        """
        strategy_type = strategy_data.get('strategy_type')
        
        if strategy_type == 'fixed_ratio':
            if not strategy_data.get('stop_loss_ratio') or not strategy_data.get('purchase_price'):
                return {
                    'valid': False,
                    'error': '固定比例止损需要设置止损比例和买入价格'
                }
        
        elif strategy_type == 'trailing':
            if not strategy_data.get('trailing_distance'):
                return {
                    'valid': False,
                    'error': '移动止损需要设置跟踪距离'
                }
        
        elif strategy_type == 'technical':
            if not strategy_data.get('technical_indicator') or not strategy_data.get('indicator_params'):
                return {
                    'valid': False,
                    'error': '技术指标止损需要设置技术指标和参数'
                }
        
        else:
            return {
                'valid': False,
                'error': '不支持的止损策略类型'
            }
        
        return {'valid': True}